Subject: Ada formatter for Scribe
From: [email-masked]
To: net.sources
Date: 2 May 85 15:08:00 GMT

We use Scribe as our standard word processing system.  We needed a way
to present Ada code within some text.  The Ada Language Reference
Manual has defined a presentation style for Ada code, with reserved
words in lower-case/boldface.  We wanted to use this style, and add
other changes to make the code more readable.  In particular, we
wanted to set comments off from Ada code.  Finally, we wanted to do
this without having to modify our Ada code by hand.

To meet these requirements, we developed a tool called "adaform".
"adaform" is a UNIX filter that accepts (well formed) Ada programs
from stdin, and adds Scribe commands.  The output is then suitable for
'Scribing'.  See the implementation section for a definition of the
Scribe environmnents used by adaform.  Invocation is "adaform < <your
input> > <your output>".  You must direct input from standard input;
adaform does not recognize the convention that a filename may be
passed as a parameter for standard input.

The following is straight text, of 3 parts: The first is a description
of the tool, followed by the LEX file, followed by our C driver
program.

Capabilities:

adaform does the following things:

1. Ada reserved words are printed in boldface.

2. Ada comments are printed in italics.

3. Comment reserved words (such as pre-conditions, history, etc.) are 
printed in bold italics.

4.  Pragma page is recognized; a new page is started after this pragma.
Additionally, every library unit is started on a new page (triggered by an
Ada 'with' clause.)

5.  Package, procedure, function, type, subtype and task names are 
indexed.  The index entry is the identifier, followed by the Ada thing
identified.  Additionally, if you use the LibraryFile MultiLevelIndex, 
then there is a secondary index by the class of item.  for instance:
	Foo, package			2
	Fred, type			1
	Frog, type			3
	Fubar, task			3
	package
	  Foo		       		2
	task
	  Fubar				3
	  Fred				1
	  Frog				3

6.  A 'hinge' is placed on each blank line.  This means that a group of
non-blank lines will be kept on one page.  An example of this is a
function declaration and its comments.

Limitations:  

adaform expects legal Ada code.  It has no error handling, and its actions
on an ill-formed program are undefined, and will probably either give you
a lex error, or garbage.

The logic used to detect a new compilation unit is very primitive.  If
adaform finds a with clause (that is not in a generic declaration), it
first inserts a page feed (@newpage).  Lines preceeding the with clause
will be placed on the preceeding page.  If there is more than one 
compilation unit in a package, and they do not have with clauses, then
they will not start on a new page.  The rule of thumb here is first,
use only 1 compilation unit per text file, and second, don't use 
preceeding comments.

If a set of non-blank lines exceeds a single page, Scribe will probably
issue an error message.  Use "pragma page" to establish page breaks.

The only 'prettyprinting' performed by adaform is that reserved words are
translated to lower case, except for EXIT, RETURN, RAISE and GOTO. Adaform
performs no formatting.  However, tabs are converted to the scribe tab
character "@\".  

Because the output from Scribe is in proportionally spaced fonts, column
alignment is not preserved.  This is most noticable when you have a comment
like:

----------------------------------
--  Here is my comment          --
----------------------------------
This usually comes out looking something like:

-----------------------
--  Here is my comment         --
-----------------------

due to the different character widths of the text and the '-' character.
To insure indentation alignment, use tabs instead of spaces.

Implementation:

adaform was created using Lex and Herm Fischer's Lex input for his Ada 
grammar.  Because it is 'lexed', and I'm not a Lexpert, it runs a little
slowly.  A driver is also requred for the Lex output, this driver writes 
out the environment standards, and then calls yylex().

There are 4 Scribe definitions needed to support adaform.  These are
definitions of the various Scribe environments used by adaform:

@define(AdaResWord=B)		makes reserved words all boldface
@define(AdaComment=I)           comments are in italics
@define(AdaCommentWord=P)       special comment reserved words in bold italics

@define(AdaCode=example, FaceCode R, LeftMargin 0, RightMargin 0, 
        font SmallBodyFont, BlankLines=HingeKeep)

			        AdaCode is an example environment, with 
				no indentation, using roman (standard) type,
				in the SmallBodyFont (10 point), and simulates
				a hinge command at each blank line.

These definitions are usually written out by the driver.

Future Features:

I hope to add scope information to the index entries, so the following 
will be the result:

	Foo, type, defined in Ralph.Bar		3
	Fred, function, defined in Ralph	2

It would be nice to make adaform more intelligent about when to start a new
page.

Finally, in conjunction with a conventional prettyprinter, I hope to solve
the alignment problem when using porportional spaced fonts.

Send bugs (hopefully with fixes) to:

			                Dave Emery
		uucp:	...princeton!siemens!emery 
		arpa:	"siemens!emery"@topaz
		us mail: Siemens Research
			 105 College Rd East
			 Princeton, NJ 08540
		ma bell: (609) 734-6568

trademarks:  Ada is a trademark of U.S. Government, AJPO.  Scribe is a
trademark of Unilogic, Inc.  UNIX is a trademark of ATT Bell Labs.
LEX DEFINITION
%{
/**************************************************************************/
/*                 Ada for SCRIBE PRINTER                                 */
/**************************************************************************/
/*------------------------------------------------------------------------*/
/* Lexical input for LEX for LALR(1) Grammar for ANSI Ada                 */
/*                                                                        */
/*              Herman Fischer                                            */
/*            Litton Data Systems                                         */
/*              March 26, 1984                                            */
/*                                                                        */
/*------------------------------------------------------------------------*/
	int	my_start   = 0; /* indicates that it's startup time       */
	int	ignore_next_subprog = 0;  /* when in a generic parameter  */
					  /* declaration, indicates that  */
					  /* the next subprogram is a     */
					  /* formal parameter, and not    */
					  /* the actual generic thing.    */
	int	funct_defn = 0;  /* indicates that this return is part    */
				 /* of a function declaration, and should */
				 /* not be capitalized			  */ 
	char	class[20];
#include "stdio.h"
#include "ctype.h"
%}

%e      3500  /* tree nodes array size    */
%p      9500  /* positions                */
%a      10000  /* transitions array size   */
%k      500   /* packed character classes */
%o      10000  /* output array size        */
%n	2000   /* states ?!		  */

%START IDENT Z COMMENT OTHERCMT PRAGMA KEEPNAME GENERICSPEC

%%
 	{BEGIN Z;}
<Z,IDENT>(ABORT|abort)|(ABS|abs)|(ACCEPT|accept)|(ACCESS|access)	|
<Z,IDENT>(ALL|all)|(AND|and)|(ARRAY|array)|(AT|at)			|
<Z,IDENT>(BEGIN|begin)|(BODY|body)|(CASE|case)|(CONSTANT|constant)	|
<Z,IDENT>(DECLARE|declare)|(DELAY|delay)|(DELTA|delta)|(DIGITS|digits)	|
<Z,IDENT>(DO|do)|(ELSE|else)|(ELSIF|elsif)|(END|end)|(ENTRY|entry)	|
<Z,IDENT>(EXCEPTION|exception)|(FOR|for)				|
<Z,IDENT>(IF|if)|(IN|in)|(IS|is)|(LIMITED|limited)|(LOOP|loop)		|
<Z,IDENT>(MOD|mod)|(NEW|new)|(NOT|not)|(NULL|null)|(OF|of)|(OR|or)	|
<Z,IDENT>(OTHERS|others)|(OUT|out)|(PRIVATE|private)			|
<Z,IDENT>(RANGE|range)|(RECORD|record)|(REM|rem)			|
<Z,IDENT>(RENAMES|renames)|(REVERSE|reverse)				|
<Z,IDENT>(SELECT|select)|(SEPARATE|separate)				|
<Z,IDENT>(TERMINATE|terminate)|(THEN|then)|(USE|use)			|
<Z,IDENT>(WHEN|when)|(WHILE|while)|(XOR|xor)				{
			MAKELOWER(yytext); hilite(yytext); BEGIN Z;	}

<Z,IDENT>(WITH|with)							{
			printf("\n@newpage\n");
			MAKELOWER(yytext); hilite(yytext);
			BEGIN Z;					}

<Z,IDENT>(GOTO|goto)|(EXIT|exit)|(RAISE|raise)				{
			MAKEUPPER(yytext); hilite(yytext); BEGIN Z;	}

<Z,IDENT>(RETURN|return)						{
			if (funct_defn) 
				funct_defn = 0;
			 else   {MAKEUPPER(yytext);
				 funct_defn = 0;};
			hilite(yytext); BEGIN Z;			}

<Z,IDENT>("=>")|("..")|("**")|(":=")|("/=")|(">=")|("<=")|("<<")	|
<Z,IDENT>(">>")|("<>")|("&")|("(")|("*")|("+")|(",")|("-")|(".")	|
<Z,IDENT>("/")|(":")|(";")|("<")|("=")|(">")|("|")			{
						ECHO; BEGIN Z;		}

<Z,IDENT>")"     		{ECHO; BEGIN IDENT;}

"\t"				{unput('\\'); unput('@');}

<Z,IDENT>(PRAGMA|pragma)	{
				MAKELOWER(yytext);
				hilite(yytext);
				BEGIN PRAGMA;
				}

<PRAGMA>[" "(@\\)]+(PAGE|page)[ (@\\)]*;	{
					ECHO;
					printf("\n@newpage\n");
					BEGIN Z;
					}

<PRAGMA>.			{
				unput(yytext[0]);
				BEGIN Z;
				}

<Z,IDENT>TYPE|type		|
<Z,IDENT>SUBTYPE|subtype	|
<Z,IDENT>TASK|task		|
<Z,IDENT>(PROCEDURE|procedure)	|
<Z,IDENT>(PACKAGE|package) 	{
				MAKELOWER(yytext);
				hilite(yytext); strcpy(class, yytext);
				BEGIN KEEPNAME;}

<Z,IDENT>(FUNCTION|function)	{
				MAKELOWER(yytext);
				hilite(yytext); strcpy(class, yytext);
				funct_defn = 1;
				BEGIN KEEPNAME;}

<KEEPNAME>[a-zA-Z][a-z_A-Z0-9]*       {
				ECHO; printf("@index(%s, %s)", yytext, class);
           printf("@indexsecondary(primary=%s, secondary=%s)", class, yytext);
				BEGIN Z;}

<Z,IDENT>(GENERIC|generic)	{
				MAKELOWER(yytext); hilite(yytext); 
				BEGIN GENERICSPEC;}

<GENERICSPEC>(WITH|with)						{
				MAKELOWER(yytext); hilite(yytext);
				ignore_next_subprog = 1;}

<GENERICSPEC>(PROCEDURE|procedure)|(FUNCTION|function)			{
				MAKELOWER(yytext); hilite(yytext);
				{if (ignore_next_subprog)
					ignore_next_subprog = 0;
				 else {
				        {if (strcmp(yytext,"procedure"))
					    strcpy(class,"generic procedure");
					 else    
					    strcpy(class,"generic function");
					    funct_defn = 1;
					};
					ignore_next_subprog = 0;
					BEGIN KEEPNAME;
				       };
				};
									}

<GENERICSPEC>(PACKAGE|package)						{
				MAKELOWER(yytext); hilite(yytext);
				strcpy(class,"generic package");
				BEGIN KEEPNAME;}

<GENERICSPEC>(ACCESS|access)|(ARRAY|array)|(CONSTANT|constant)		|
<GENERICSPEC>(DELTA|delta)|(DIGITS|digits)|(IN|in)|(IS|is)		|
<GENERICSPEC>(LIMITED|limited)|(OF|of)|(OTHERS|others)|(OUT|out)	|
<GENERICSPEC>(PRIVATE|private)|(RANGE|range)|(RETURN|return)		|
<GENERICSPEC>(SUBTYPE|subtype)|(TYPE|type)				{
				MAKELOWER(yytext); hilite(yytext);	}

<GENERICSPEC>[a-zA-Z][a-z_A-Z0-9]*	{ECHO;}

<GENERICSPEC>.				{ECHO;}

<IDENT>\'  {ECHO; BEGIN Z;}   /* type mark only */

<Z,IDENT>[a-zA-Z][a-z_A-Z0-9]*       {ECHO; BEGIN IDENT; }

<Z,IDENT>[0-9][0-9_]*([.][0-9_]+)?([Ee][-+]?[0-9_]+)?  {
						         ECHO; BEGIN Z; }

<Z,IDENT>[0-9][0-9_]*#[0-9a-fA-F_]+([.][0-9a-fA-F_]+)?#([Ee][-+]?[0-9_]+)? {
			      ECHO; BEGIN Z; }

<Z,IDENT>\"([^\"]*(\"\")*)*\" {ECHO; BEGIN Z; }

<Z>\'([^\']|\'\')\'    {ECHO; BEGIN Z; }

<Z,IDENT>[" "(@\\)]   {ECHO;}          /* ignore spaces and tabs */

<Z,IDENT>.       {ECHO; printf("?? lexical error [%s] ??\n(@\\)", yytext);}

<Z,IDENT>"--"([" "(@\\)]*)	{ECHO; BEGIN COMMENT;}

<COMMENT,OTHERCMT>"-"+	{ECHO;}
<COMMENT,OTHERCMT>[\n]	{ECHO; BEGIN Z;}

<COMMENT>(("Function"[" "(@\\)]*":")|("Reference"[" "(@\\)]*":"))		|
<COMMENT>(("History"[" "(@\\)]*":")|("Exceptions"[" "(@\\)]*":"))		|
<COMMENT>(("Authorization"[" "(@\\)]*":")|("Pre-conditions"[" "(@\\)]*":"))	|
<COMMENT>(("Post-conditions"[" "(@\\)]*":")|("EXCEPTIONS"[" "(@\\)]*":"))	{
				printf("@AdaCommentWord(%s)", yytext);
				BEGIN OTHERCMT;
									}

<COMMENT>.	{
		 unput(yytext[0]); 
		 BEGIN OTHERCMT;
		}

<OTHERCMT>.*	{
		 printf("@begin(AdaComment)%s@end(AdaComment)", yytext);
		 BEGIN Z;
		}

%%
hilite(it)	
	char 	*it;
{printf("@AdaResWord(%s)", it);}

MAKEUPPER(s)
	char	*s;
{
	for (; *s != NULL; ++s)
		*s = (islower(*s) ? toupper(*s) : *s);
}
MAKELOWER(s)
	char	*s;
{
	for (; *s != NULL; ++s)
		*s = (isupper(*s) ? tolower(*s) : *s);
}

end of LEX DEFINITION
OUR DRIVER:

main()

{
	printf("@comment(formatted with adaform v 1.3)\n");
	printf("@define(AdaResWord=B)\n");
	printf("@define(AdaComment=I)\n");
	printf("@define(AdaCommentWord=P)\n");
printf("@define(AdaCode=example, FaceCode R, LeftMargin 0, RightMargin 0,\n");
	printf("\t font SmallBodyFont, blanklines=hingekeep)\n");
	printf("@begin(AdaCode)\n");
	printf("@TabClear\n@TabDivide(10)\n");
	yylex();
	printf("@end(AdaCode)\n");
}

end of OUR DRIVER